from Scanner import Scanner
from jsAst.types import *
from tokenDef import *

def ignoreNone(cmd,x):
    if x==None:
        return
    if isinstance(x,list):
        cmd.extend(x)
    else:
        cmd.append(x)
class Parser:
    def __init__(self,path) -> None:
        content=open(path,'r').read()
        self.scanner=Scanner(content)
        self.mapping={
            'for':self._parseFor,
            'while':self._parseWhile,
            'if':self._parseIf,
            'function':self._parseFunc,
            'do':self._parseDo,
            'let':self._parseLet,
            'var':self._parseLet,
        }
        self.ignoreLF=0

    def genAst(self):
        cmds=[]
        while True:
            token=self.scanner.readToken()
            if token==None:
                break
            if isinstance(token,Node):
                if token.op in self.mapping:#控制命令
                    ignoreNone(cmds,self.mapping[token.op]())
                    continue
                elif token.op in END_TOKEN:
                    continue
            self.scanner.retToken(token)
            ignoreNone(cmds,self.statement())
        return {'cmds':cmds}

    def _parseLet(self):
        res=[]
        while True:
            item=self.statement()
            if isinstance(item,jVar):
                item=Node(item.value,None,TOKEN['ASSIGN'])
            res.append(item)
            name=self.scanner.readToken()
            if name.op==TOKEN['SEMI']:
                continue
            elif name.op==TOKEN['COMMA'] or name.op==TOKEN['LF']:
                break
            else:
                raise ErrorSyntax(self,'except a var name but '+name)
        return res

    def _parseFor(self):
        pass
    
    def _parseWhile(self):
        pass

    def _parseIf(self):
        pass

    def _parseFunc(self):
        pass

    def _parseDo(self):
        pass

    def statement(self):
        cur=None
        while True:
            token=self.scanner.readToken()
            if token is None:
                while isinstance(cur,AstNode) and cur.parent!=None:
                    cur=cur.parent
                return cur
            elif isinstance(token,Data):
                if cur is None:cur=token
                elif cur.right is None:
                    cur.right=token
                    while cur.left is None and cur.parent is not None:#跳出一元运算符
                        cur=cur.parent
                else:
                    raise ErrorSyntax(self,'unexcept '+token)
            elif token.op in END_TOKEN:#闭合括号需要由外部来处理
                if self.ignoreLF>0 and token.op==TOKEN['LF']:continue
                while isinstance(cur,AstNode) and cur.parent!=None:
                    cur=cur.parent
                self.scanner.retToken(token)
                return cur
            else:
                pri=OPT_PRIORITY[token.op]
                if isinstance(cur,Data):
                    if token.op==TOKEN['LPT']:
                        params=self._dealParams(token)
                        cur=AstNode(cur,params,op=token.op,line=self.scanner.line)
                    elif token.op==TOKEN['LMBR']:
                        self.ignoreLF+=1
                        inner=self.statement()
                        self.ignoreLF-=1
                        cur=AstNode(cur,inner,op=token.op,line=self.scanner.line)
                        valid=self.scanner.readToken()
                        if valid.op!=TOKEN['RMBR']:
                            raise ErrorSyntax(self,'unclosed '+token.op)
                    else:
                        cur=AstNode(cur,op=token.op,line=self.scanner.line)
                else:
                    if pri==20:#需要特殊处理的运算符
                        if token.op==TOKEN['DOT']:
                            if cur.right is None:
                                raise ErrorSyntax(self,'except data but'+token)
                            cur.right=AstNode(cur.right,op=token.op,parent=cur,line=self.scanner.line)
                            cur=cur.right
                        elif token.op==TOKEN['LPT'] or token.op==TOKEN['LMBR']:#括号可能是运算符，也可能是函数调用，需要处理
                            if cur.right is None:#运算符
                                self.ignoreLF+=1
                                cur.right=self.statement()
                                self.ignoreLF-=1
                                if not isinstance(cur.right,Data):
                                    cur.right.parent=cur
                                valid=self.scanner.readToken()
                                if token.op==TOKEN['LPT'] and valid.op!=TOKEN['RPT']:
                                    raise ErrorSyntax(self,'unclosed '+token.op)
                                if token.op==TOKEN['LMBR'] and valid.op!=TOKEN['RMBR']:
                                    raise ErrorSyntax(self,'unclosed '+token.op)
                            elif token.op==TOKEN['LMBR']:
                                self.ignoreLF+=1
                                inner=self.statement()
                                self.ignoreLF-=1
                                cur.right=AstNode(cur.right,inner,op=token.op,parent=cur,line=self.scanner.line)
                                valid=self.scanner.readToken()
                                if valid.op!=TOKEN['RMBR']:
                                    raise ErrorSyntax(self,'unclosed '+token.op)
                            else:
                                params=self._dealParams(token)
                                cur.right=AstNode(cur.right,params,op=token.op,parent=cur,line=self.scanner.line)
                        elif token.op==TOKEN['RPT'] or token.op==TOKEN['RMBR']:
                            if cur is None or cur.right is None:
                                raise ErrorSyntax(self,'unexcept end')
                            while cur.parent is not None:
                                cur=cur.parent
                            return cur
                    elif cur.right is None:
                        #判断是不是右向运算符
                        if token.op in LEFT_OPT:
                            cur.right=AstNode(op=token.op,parent=cur,line=self.scanner.line)
                            cur=cur.right
                        else:
                            raise ErrorSyntax(self,'except data but'+token)
                    else:
                        curPri=OPT_PRIORITY[cur.op]
                        if curPri==pri:
                            next=AstNode(cur,op=token.op,parent=cur.parent,line=self.scanner.line)
                            cur.parent.right=next
                            cur.parent=next
                            cur=next
                        elif curPri<pri:
                            cur.right=AstNode(cur.right,op=token.op,parent=cur,line=self.scanner.line)
                            cur=cur.right
                        else:#当前运算符优先级更低，需要找父级优先级相同或者更低的
                            while cur.parent is not None:
                                cur=cur.parent
                                curPri=OPT_PRIORITY[cur.op]
                                if curPri<=pri:break
                            if curPri<pri:
                                cur.right=AstNode(cur.right,op=token.op,parent=cur,line=self.scanner.line)
                                cur=cur.right
                            else:
                                next=AstNode(cur,op=token.op,parent=cur.parent,line=self.scanner.line)
                                cur.parent.right=next
                                cur.parent=next
                                cur=next
                                
    def _dealParams(self,token):#读取函数参数
        valid=self.scanner.readToken()
        if isinstance(valid,Node) and valid.op==TOKEN['RPT']:
            return []
        self.scanner.retToken(valid)
        params=[]
        while True:
            self.ignoreLF+=1
            inner=self.statement()
            self.ignoreLF-=1
            params.append(inner)
            valid=self.scanner.readToken()
            if valid.op==TOKEN['SEMI']:
                continue
            elif valid.op==TOKEN['RPT']:
                return params
            else:
                raise ErrorSyntax(self,'unclosed '+token.op)

class ErrorSyntax(Exception):
    def __init__(self,p,msg) -> None:
        self.line=p.scanner.line
        self.msg=msg
    def __str__(self) -> str:
        return f'error syntax at line {self.line}:{self.msg}'

if __name__ == '__main__':
    a=Parser('test.js')
    res=a.genAst()
    print(res)